{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **作者**:\tValentin Haenel\n",
    "\n",
    "本章包含了许多可以在Python使用原生代码（主要是C/C++）方式的*介绍*，这个过程通常被称为*封装*。本章的目的是给你有哪些技术存在已经各自有优劣式的一点儿感觉，这样你可以根据你的具体需求选择适合的方式。无论如何，只要你开始做封装，你几乎都必然需要咨询你选定技术的文档。\n",
    "\n",
    "---\n",
    "章节内容\n",
    "\n",
    "    简介\n",
    "    Python-C-Api\n",
    "    Ctypes\n",
    "    SWIG\n",
    "    Cython\n",
    "    总结\n",
    "    进一步阅读和参考\n",
    "    练习\n",
    "\n",
    "---\n",
    "\n",
    "## 2.8.1 简介\n",
    "\n",
    "本章将涵盖一下技术：\n",
    "\n",
    "- Python-C-Api\n",
    "- Ctypes\n",
    "- SWIG (简化封装器和接口生成器)\n",
    "- Cython\n",
    "\n",
    "这四种技术可能是最知名的，其中Cython可能是最高级的，并且你应该最优先使用它。其他的技术也很重要，如果你想要从不同点角度理解封装问题。之前提到过，还有其他的替代技术，但是，理解了以上这些基础的，你就可以评估你选择的技术是否满足你的需求。\n",
    "\n",
    "在评估技术时，下列标准会有帮助:\n",
    "- 需要额外的库吗?\n",
    "- 代码可以自动生成?\n",
    "- 是否需要编译?\n",
    "- 与Numpy数组交互是否有良好的支持?\n",
    "- 是否支持C++?\n",
    "\n",
    "在你动手前，应该先考虑一下使用情景。在于原生代码交互时，通常来自于两个应用场景:\n",
    "\n",
    "- 需要利用C/C++中现存的代码，或者是因为它已经存在，或者是因为它更快。\n",
    "- Python代码太慢，将内部循环变成原生代码\n",
    "\n",
    "每个技术都使用来自math.h的`cos`函数的封装来进行演示。尽管这是一个无聊例子，但是它确实给我们很好的演示了封装方法的基础，因为每个技术也包含了一定程度Numpy支持，这也用计算一些数组来计算consine的例子来演示。\n",
    "\n",
    "最后，两个小警示:\n",
    "\n",
    "- 所有这些方法在Python解释器中都可能崩溃(细分错误)，因为在C代码中的错误。\n",
    "- 所有的例子都在Linux中完成，他们应该在其他操作系统中也可用。\n",
    "- 在大多数例子中，你都会需要C编译器。\n",
    "\n",
    "## 2.8.2 Python-C-Api\n",
    "\n",
    "Python-C-API是标准Python解释器（即CPython）的基础。使用这个API可以在C和C++中写Python扩展模块。很明显，由于语言兼容性的优点，这些扩展模块可以调用任何用C或者C++写的函数。\n",
    "\n",
    "当使用Python-C-API时，人们通常写许多样板化的代码，首先解析函数接收的参数，然后构建返回的类型。\n",
    "\n",
    "**优点**\n",
    "- 不需要额外的库\n",
    "- 许多系层的控制\n",
    "- C++完全可用\n",
    "\n",
    "**不足**\n",
    "- 可以需要一定的努力\n",
    "- 高代码成本\n",
    "- 必须编译\n",
    "- 高维护成本\n",
    "- 如果C-API改变无法向前兼容Python版本\n",
    "- 引用计数错误很容易出现，但是很难被跟踪。\n",
    "\n",
    "\n",
    "---\n",
    "\n",
    "**注意** 此处的Python-C-Api例子主要是用来演示。许多其他例子的确依赖它，因此，对于它如何工作有一个高层次的理解。在99%的使用场景下，使用替代技术会更好。\n",
    "\n",
    "---\n",
    "\n",
    "---\n",
    "\n",
    "**注意** 因为引用计数很容易出现然而很难被跟踪，任何需要使用Python C-API的人都应该阅读[官方Python文档关于对象、类型和引用计数的部分](https://docs.python.org/2/c-api/intro.html#objects-types-and-reference-counts)。此外，有一个名为[cpychecker](https://gcc-python-plugin.readthedocs.org/en/latest/cpychecker.html)的工具可以发现引用计数的常见错误。\n",
    "\n",
    "---\n",
    "\n",
    "### 2.8.2.1 例子\n",
    "\n",
    "下面的C扩展模块，让来自标准`math`库的`cos`函数在Python中可用:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "/*  用Python-C-API封装来自math.h的cos函数的例子 */\n",
    "\n",
    "#include <Python.h>\n",
    "#include <math.h>\n",
    "\n",
    "/*  wrapped cosine function */\n",
    "static PyObject* cos_func(PyObject* self, PyObject* args)\n",
    "{\n",
    "    double value;\n",
    "    double answer;\n",
    "\n",
    "    /*  parse the input, from python float to c double */\n",
    "    if (!PyArg_ParseTuple(args, \"d\", &value))\n",
    "        return NULL;\n",
    "    /* if the above function returns -1, an appropriate Python exception will\n",
    "     * have been set, and the function simply returns NULL\n",
    "     */\n",
    "\n",
    "    /* call cos from libm */\n",
    "    answer = cos(value);\n",
    "\n",
    "    /*  construct the output from cos, from c double to python float */\n",
    "    return Py_BuildValue(\"f\", answer);\n",
    "}\n",
    "\n",
    "/*  define functions in module */\n",
    "static PyMethodDef CosMethods[] =\n",
    "{\n",
    "     {\"cos_func\", cos_func, METH_VARARGS, \"evaluate the cosine\"},\n",
    "     {NULL, NULL, 0, NULL}\n",
    "};\n",
    "\n",
    "/* module initialization */\n",
    "PyMODINIT_FUNC\n",
    "\n",
    "initcos_module(void)\n",
    "{\n",
    "     (void) Py_InitModule(\"cos_module\", CosMethods);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "如你所见，有许多样板，既包括 «massage» 的参数和return类型以及模块初始化。尽管随着扩展的增长，这些东西中的一些是分期偿还，模板每个函数需要的模板还是一样的。\n",
    "\n",
    "标准python构建系统`distutils`支持从`setup.py`编译C-扩展, 非常方便:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from distutils.core import setup, Extension\n",
    "\n",
    "# 定义扩展模块\n",
    "cos_module = Extension('cos_module', sources=['cos_module.c'])\n",
    "\n",
    "# 运行setup\n",
    "setup(ext_modules=[cos_module])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "这可以被编译:\n",
    "\n",
    "```\n",
    "\n",
    "$ cd advanced/interfacing_with_c/python_c_api\n",
    "\n",
    "$ ls\n",
    "cos_module.c  setup.py\n",
    "\n",
    "$ python setup.py build_ext --inplace\n",
    "running build_ext\n",
    "building 'cos_module' extension\n",
    "creating build\n",
    "creating build/temp.linux-x86_64-2.7\n",
    "gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/include/python2.7 -c cos_module.c -o build/temp.linux-x86_64-2.7/cos_module.o\n",
    "gcc -pthread -shared build/temp.linux-x86_64-2.7/cos_module.o -L/home/esc/anaconda/lib -lpython2.7 -o /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/python_c_api/cos_module.so\n",
    "\n",
    "$ ls\n",
    "build/  cos_module.c  cos_module.so  setup.py\n",
    "\n",
    "```\n",
    "\n",
    "- `build_ext` 是构建扩展模块\n",
    "- `--inplace` 将把编译后的扩展模块输出到当前目录\n",
    "\n",
    "文件`cos_module.so`包含编译后的扩展，我们可以在IPython解释器中加载它:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "In [1]: import cos_module\n",
    "\n",
    "In [2]: cos_module?\n",
    "Type:       module\n",
    "String Form:<module 'cos_module' from 'cos_module.so'>\n",
    "File:       /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/python_c_api/cos_module.so\n",
    "Docstring:  <no docstring>\n",
    "\n",
    "In [3]: dir(cos_module)\n",
    "Out[3]: ['__doc__', '__file__', '__name__', '__package__', 'cos_func']\n",
    "\n",
    "In [4]: cos_module.cos_func(1.0)\n",
    "Out[4]: 0.5403023058681398\n",
    "\n",
    "In [5]: cos_module.cos_func(0.0)\n",
    "Out[5]: 1.0\n",
    "\n",
    "In [6]: cos_module.cos_func(3.14159265359)\n",
    "Out[7]: -1.0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在我们看一下这有多强壮:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "In [10]: cos_module.cos_func('foo')\n",
    "---------------------------------------------------------------------------\n",
    "TypeError                                 Traceback (most recent call last)\n",
    "<ipython-input-10-11bee483665d> in <module>()\n",
    "----> 1 cos_module.cos_func('foo')\n",
    "\n",
    "TypeError: a float is required"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.8.2.2. Numpy 支持\n",
    "\n",
    "Numpy模拟Python-C-API, 自身也实现了C-扩展, 产生了Numpy-C-API。这个API可以被用来创建和操作来自C的Numpy数组, 当写一个自定义的C-扩展。也可以看一下:参考:`advanced_numpy`。\n",
    "\n",
    "---\n",
    "**注意** 如果你确实需要使用Numpy C-API参考关于[Arrays](http://docs.scipy.org/doc/numpy/reference/c-api.array.html)和[Iterators](http://docs.scipy.org/doc/numpy/reference/c-api.iterator.html)的文档。\n",
    "\n",
    "---\n",
    "\n",
    "下列的例子显示如何将Numpy数组作为参数传递给函数，以及如果使用(旧)Numpy-C-API在Numpy数组上迭代。它只是将一个数组作为参数应用到来自`math.h`的cosine函数，并且返回生成的新数组。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "/*  使用Numpy-C-API封装来自math.h的cos函数 . */\n",
    "\n",
    "#include <Python.h>\n",
    "#include <numpy/arrayobject.h>\n",
    "#include <math.h>\n",
    "\n",
    "/*  封装cosine函数 */\n",
    "static PyObject* cos_func_np(PyObject* self, PyObject* args)\n",
    "{\n",
    "\n",
    "    PyArrayObject *in_array;\n",
    "    PyObject      *out_array;\n",
    "    NpyIter *in_iter;\n",
    "    NpyIter *out_iter;\n",
    "    NpyIter_IterNextFunc *in_iternext;\n",
    "    NpyIter_IterNextFunc *out_iternext;\n",
    "\n",
    "    /*  parse single numpy array argument */\n",
    "    if (!PyArg_ParseTuple(args, \"O!\", &PyArray_Type, &in_array))\n",
    "        return NULL;\n",
    "\n",
    "    /*  construct the output array, like the input array */\n",
    "    out_array = PyArray_NewLikeArray(in_array, NPY_ANYORDER, NULL, 0);\n",
    "    if (out_array == NULL)\n",
    "        return NULL;\n",
    "\n",
    "    /*  create the iterators */\n",
    "    in_iter = NpyIter_New(in_array, NPY_ITER_READONLY, NPY_KEEPORDER,\n",
    "                             NPY_NO_CASTING, NULL);\n",
    "    if (in_iter == NULL)\n",
    "        goto fail;\n",
    "\n",
    "    out_iter = NpyIter_New((PyArrayObject *)out_array, NPY_ITER_READWRITE,\n",
    "                          NPY_KEEPORDER, NPY_NO_CASTING, NULL);\n",
    "    if (out_iter == NULL) {\n",
    "        NpyIter_Deallocate(in_iter);\n",
    "        goto fail;\n",
    "    }\n",
    "\n",
    "    in_iternext = NpyIter_GetIterNext(in_iter, NULL);\n",
    "    out_iternext = NpyIter_GetIterNext(out_iter, NULL);\n",
    "    if (in_iternext == NULL || out_iternext == NULL) {\n",
    "        NpyIter_Deallocate(in_iter);\n",
    "        NpyIter_Deallocate(out_iter);\n",
    "        goto fail;\n",
    "    }\n",
    "    double ** in_dataptr = (double **) NpyIter_GetDataPtrArray(in_iter);\n",
    "    double ** out_dataptr = (double **) NpyIter_GetDataPtrArray(out_iter);\n",
    "\n",
    "    /*  iterate over the arrays */\n",
    "    do {\n",
    "        **out_dataptr = cos(**in_dataptr);\n",
    "    } while(in_iternext(in_iter) && out_iternext(out_iter));\n",
    "\n",
    "    /*  clean up and return the result */\n",
    "    NpyIter_Deallocate(in_iter);\n",
    "    NpyIter_Deallocate(out_iter);\n",
    "    Py_INCREF(out_array);\n",
    "    return out_array;\n",
    "\n",
    "    /*  in case bad things happen */\n",
    "    fail:\n",
    "        Py_XDECREF(out_array);\n",
    "        return NULL;\n",
    "}\n",
    "\n",
    "/*  在模块中定义函数 */\n",
    "static PyMethodDef CosMethods[] =\n",
    "{\n",
    "     {\"cos_func_np\", cos_func_np, METH_VARARGS,\n",
    "         \"evaluate the cosine on a numpy array\"},\n",
    "     {NULL, NULL, 0, NULL}\n",
    "};\n",
    "\n",
    "/* 模块初始化 */\n",
    "PyMODINIT_FUNC\n",
    "initcos_module_np(void)\n",
    "{\n",
    "     (void) Py_InitModule(\"cos_module_np\", CosMethods);\n",
    "     /* IMPORTANT: this must be called */\n",
    "     import_array();\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要编译这个模块，我们可以再用`distutils`。但是我们需要通过使用func:numpy.get_include确保包含了Numpy头部:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from distutils.core import setup, Extension\n",
    "import numpy\n",
    "\n",
    "# define the extension module\n",
    "cos_module_np = Extension('cos_module_np', sources=['cos_module_np.c'],\n",
    "                          include_dirs=[numpy.get_include()])\n",
    "\n",
    "# run the setup\n",
    "setup(ext_modules=[cos_module_np])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要说服我们自己这个方式确实有效，我们来跑一下下面的测试脚本:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import cos_module_np\n",
    "import numpy as np\n",
    "import pylab\n",
    "\n",
    "x = np.arange(0, 2 * np.pi, 0.1)\n",
    "y = cos_module_np.cos_func_np(x)\n",
    "pylab.plot(x, y)\n",
    "pylab.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这会产生以下的图像:\n",
    "\n",
    "![](http://www.scipy-lectures.org/_images/test_cos_module_np.png)\n",
    "\n",
    "## 2.8.3. Ctypes\n",
    "\n",
    "Ctypes是Python的一个外来函数库。它提供了C兼容的数据类型，并且允许在DLLs或者共享的库中调用函数。它可以用来在纯Python中封装这些库。\n",
    "\n",
    "**优点**\n",
    "- Python标准库的一部分\n",
    "- 不需要编译\n",
    "- 代码封装都是在Python中\n",
    "\n",
    "**不足**\n",
    "- 需要代码作为一个共享的库（粗略地说，在windows中是 \\*.dll，在Linux中是\\*.so，在Mac OSX中是 \\*.dylib）\n",
    "- 对C++支持并不好\n",
    "\n",
    "### 2.8.3.1 例子\n",
    "\n",
    "如前面提到的，代码封装完全在Python中。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "\"\"\" 用ctypes封装来自math.h的 cos 函数。 \"\"\"\n",
    "\n",
    "import ctypes\n",
    "from ctypes.util import find_library\n",
    "\n",
    "# find and load the library\n",
    "libm = ctypes.cdll.LoadLibrary(find_library('m'))\n",
    "# set the argument type\n",
    "libm.cos.argtypes = [ctypes.c_double]\n",
    "# set the return type\n",
    "libm.cos.restype = ctypes.c_double\n",
    "\n",
    "\n",
    "def cos_func(arg):\n",
    "    ''' 封装math.h cos函数 '''\n",
    "    return libm.cos(arg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 寻找和加载库可能非常依赖于你的操作系统，检查[文档](https://docs.python.org/2/library/ctypes.html#loading-dynamic-link-libraries)来了解细节\n",
    "- 这可能有些欺骗性，因为math库在系统中已经是编译模式。如果你想要封装一个内置的库，需要先编译它，可能需要或者不需要额外的工作。\n",
    "我们现在可以像前面一样使用这个库："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "In [1]: import cos_module\n",
    "\n",
    "In [2]: cos_module?\n",
    "Type:       module\n",
    "String Form:<module 'cos_module' from 'cos_module.py'>\n",
    "File:       /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/ctypes/cos_module.py\n",
    "Docstring:  <no docstring>\n",
    "\n",
    "In [3]: dir(cos_module)\n",
    "Out[3]:\n",
    "['__builtins__',\n",
    " '__doc__',\n",
    " '__file__',\n",
    " '__name__',\n",
    " '__package__',\n",
    " 'cos_func',\n",
    " 'ctypes',\n",
    " 'find_library',\n",
    " 'libm']\n",
    "\n",
    "In [4]: cos_module.cos_func(1.0)\n",
    "Out[4]: 0.5403023058681398\n",
    "\n",
    "In [5]: cos_module.cos_func(0.0)\n",
    "Out[5]: 1.0\n",
    "\n",
    "In [6]: cos_module.cos_func(3.14159265359)\n",
    "Out[6]: -1.0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### 2.8.3.2 Numpy支持\n",
    "\n",
    "Numpy包含一些与ctypes交互的支持。特别是支持将特定Numpy数组属性作为ctypes数据类型研究，并且有函数可以将C数组和Numpy数据互相转换。\n",
    "\n",
    "更多信息，可以看一下Numpy手册的对应部分或者`numpy.ndarray.ctypes`和`numpy.ctypeslib`的API文档。\n",
    "\n",
    "对于下面的例子，让我们假设一个C函数，输入输出都是一个数组，计算输入数组的cosine并将结果输出为一个数组。\n",
    "\n",
    "库包含下列头文件（尽管在这个例子中并不是必须这样，为了完整，我们还是把这一步列出来):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "void cos_doubles(double * in_array, double * out_array, int size);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "这个函数实现在下列的C源文件中:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#include <math.h>\n",
    "\n",
    "/*  Compute the cosine of each element in in_array, storing the result in\n",
    " *  out_array. */\n",
    "void cos_doubles(double * in_array, double * out_array, int size){\n",
    "    int i;\n",
    "    for(i=0;i<size;i++){\n",
    "        out_array[i] = cos(in_array[i]);\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "并且因为这个库是纯C的，我们不能使用`distutils`来编译，但是，必须使用`make`和`gcc`的组合:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "m.PHONY : clean\n",
    "\n",
    "libcos_doubles.so : cos_doubles.o\n",
    "\tgcc -shared -Wl,-soname,libcos_doubles.so -o libcos_doubles.so cos_doubles.o\n",
    "\n",
    "cos_doubles.o : cos_doubles.c\n",
    "\tgcc -c -fPIC cos_doubles.c -o cos_doubles.o\n",
    "\n",
    "clean :\n",
    "\t-rm -vf libcos_doubles.so cos_doubles.o cos_doubles.pyc"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，我们可以将这个库编译到共享的库 (on Linux)`libcos_doubles.so`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "$ ls\n",
    "cos_doubles.c  cos_doubles.h  cos_doubles.py  makefile  test_cos_doubles.py\n",
    "$ make\n",
    "gcc -c -fPIC cos_doubles.c -o cos_doubles.o\n",
    "gcc -shared -Wl,-soname,libcos_doubles.so -o libcos_doubles.so cos_doubles.o\n",
    "$ ls\n",
    "cos_doubles.c  cos_doubles.o   libcos_doubles.so*  test_cos_doubles.py\n",
    "cos_doubles.h  cos_doubles.py  makefile"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在我们可以继续通过ctypes对Numpy数组的直接支持（一定程度上）来封装这个库:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "\"\"\" 封装一个使用numpy.ctypeslib接受C双数组作为输入的例子。\"\"\"\n",
    "\n",
    "import numpy as np\n",
    "import numpy.ctypeslib as npct\n",
    "from ctypes import c_int\n",
    "\n",
    "# cos_doubles的输入类型\n",
    "# 必须是双数组, 有相邻的单维度\n",
    "array_1d_double = npct.ndpointer(dtype=np.double, ndim=1, flags='CONTIGUOUS')\n",
    "\n",
    "# 加载库，运用numpy机制\n",
    "libcd = npct.load_library(\"libcos_doubles\", \".\")\n",
    "\n",
    "# 设置反馈类型和参数类型\n",
    "libcd.cos_doubles.restype = None\n",
    "libcd.cos_doubles.argtypes = [array_1d_double, array_1d_double, c_int]\n",
    "\n",
    "\n",
    "def cos_doubles_func(in_array, out_array):\n",
    "    return libcd.cos_doubles(in_array, out_array, len(in_array))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 注意临近单维度Numpy数组的固有限制，因为C函数需要这类的缓存器。\n",
    "- 也需要注意输出数组也需要是预分配的，例如[numpy.zeros()](http://docs.scipy.org/doc/numpy/reference/generated/numpy.zeros.html#numpy.zeros)和函数将用它的缓存器来写。\n",
    "- 尽管`cos_doubles`函数的原始签名是`ARRAY`, `ARRAY`, `int`最终的`cos_doubles_func`需要两个Numpy数组作为参数。\n",
    "\n",
    "并且，和前面一样，我们我要为自己证明一下它是有效的:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pylab\n",
    "import cos_doubles\n",
    "\n",
    "x = np.arange(0, 2 * np.pi, 0.1)\n",
    "y = np.empty_like(x)\n",
    "\n",
    "cos_doubles.cos_doubles_func(x, y)\n",
    "pylab.plot(x, y)\n",
    "pylab.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](http://www.scipy-lectures.org/_images/test_cos_doubles.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.8.4 SWIG\n",
    "\n",
    "[SWIG](http://www.swig.org/), 简化封装接口生成器，是一个联接用C和C++写的程序与需要高级程序语言，包括Python的软件开发工具。SWIG的重点在于它可以为你自动生成封装器代码。尽管从编程时间上来说这是一个优势，但是同时也是一个负担。生成的文件通常很大，并且可能并不是人类可读的，封装过程造成的多层间接引用可能很难理解。\n",
    "\n",
    "**注意** 自动生成的C代码使用Python-C-Api。\n",
    "\n",
    "**优势**\n",
    "- 给定头部可以自动封装整个库\n",
    "- 在C++中表现良好\n",
    "\n",
    "**不足**\n",
    "- 自动生成的文件很庞大\n",
    "- 如果出错很难debug\n",
    "- 陡峭的学习曲线\n",
    "\n",
    "### 2.8.4.1 例子\n",
    "\n",
    "让我们想象我们的`cos`函数存在于用C写的`cos_module`中，包含在源文件`cos_module.c`中:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#include <math.h>\n",
    "\n",
    "double cos_func(double arg){\n",
    "    return cos(arg);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "头文件`cos_module.h`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "double cos_func(double arg);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "尽管我们的目的是将`cos_func`暴露给Python。要用SWIG来完成这个目的，我们需要写一个包含SWIG指导的接口文件。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "/*  Example of wrapping cos function from math.h using SWIG. */\n",
    "\n",
    "%module cos_module\n",
    "%{\n",
    "    /* the resulting C file should be built as a python extension */\n",
    "    #define SWIG_FILE_WITH_INIT\n",
    "    /*  Includes the header in the wrapper code */\n",
    "    #include \"cos_module.h\"\n",
    "%}\n",
    "/*  Parse the header file to generate wrappers */\n",
    "%include \"cos_module.h\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如你所见，这里不需要太多的代码。对于这个简单的例子，它简单到只需要在接口文件中包含一个头文件，来向Python暴露函数。但是，SWIG确实允许更多精细包含或者排除在头文件中发现的函数，细节检查一下文档。\n",
    "\n",
    "生成编译的封装器是一个两阶段的过程:\n",
    "\n",
    "- 在接口文件上运行`swig`可执行文件来生成文件`cos_module_wrap.c`, 其源文件是自动生成的Python C-extension和`cos_module.py`, 是自动生成的Python模块。\n",
    "\n",
    "- 编译`cos_module_wrap.c`到`_cos_module.so`。幸运的，`distutils`知道如何处理SWIG接口文件, 因此我们的`setup.py`是很简单的:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from distutils.core import setup, Extension\n",
    "\n",
    "setup(ext_modules=[Extension(\"_cos_module\",\n",
    "      sources=[\"cos_module.c\", \"cos_module.i\"])])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "$ cd advanced/interfacing_with_c/swig\n",
    "\n",
    "$ ls\n",
    "cos_module.c  cos_module.h  cos_module.i  setup.py\n",
    "\n",
    "$ python setup.py build_ext --inplace\n",
    "running build_ext\n",
    "building '_cos_module' extension\n",
    "swigging cos_module.i to cos_module_wrap.c\n",
    "swig -python -o cos_module_wrap.c cos_module.i\n",
    "creating build\n",
    "creating build/temp.linux-x86_64-2.7\n",
    "gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/include/python2.7 -c cos_module.c -o build/temp.linux-x86_64-2.7/cos_module.o\n",
    "gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/include/python2.7 -c cos_module_wrap.c -o build/temp.linux-x86_64-2.7/cos_module_wrap.o\n",
    "gcc -pthread -shared build/temp.linux-x86_64-2.7/cos_module.o build/temp.linux-x86_64-2.7/cos_module_wrap.o -L/home/esc/anaconda/lib -lpython2.7 -o /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/swig/_cos_module.so\n",
    "\n",
    "$ ls\n",
    "build/  cos_module.c  cos_module.h  cos_module.i  cos_module.py  _cos_module.so*  cos_module_wrap.c  setup.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以像前面的例子中那样加载和运行`cos_module`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "In [1]: import cos_module\n",
    "\n",
    "In [2]: cos_module?\n",
    "Type:       module\n",
    "String Form:<module 'cos_module' from 'cos_module.py'>\n",
    "File:       /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/swig/cos_module.py\n",
    "Docstring:  <no docstring>\n",
    "\n",
    "In [3]: dir(cos_module)\n",
    "Out[3]:\n",
    "['__builtins__',\n",
    " '__doc__',\n",
    " '__file__',\n",
    " '__name__',\n",
    " '__package__',\n",
    " '_cos_module',\n",
    " '_newclass',\n",
    " '_object',\n",
    " '_swig_getattr',\n",
    " '_swig_property',\n",
    " '_swig_repr',\n",
    " '_swig_setattr',\n",
    " '_swig_setattr_nondynamic',\n",
    " 'cos_func']\n",
    "\n",
    "In [4]: cos_module.cos_func(1.0)\n",
    "Out[4]: 0.5403023058681398\n",
    "\n",
    "In [5]: cos_module.cos_func(0.0)\n",
    "Out[5]: 1.0\n",
    "\n",
    "In [6]: cos_module.cos_func(3.14159265359)\n",
    "Out[6]: -1.0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来我们测试一下强壮性，我们看到我们可以获得一个更多的错误信息 (虽然, 严格来讲在Python中没有double类型):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "In [7]: cos_module.cos_func('foo')\n",
    "---------------------------------------------------------------------------\n",
    "TypeError                                 Traceback (most recent call last)\n",
    "<ipython-input-7-11bee483665d> in <module>()\n",
    "----> 1 cos_module.cos_func('foo')\n",
    "\n",
    "TypeError: in method 'cos_func', argument 1 of type 'double'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.8.4.2 Numpy 支持\n",
    "\n",
    "Numpy在`numpy.i`文件中提供了[SWIG的支持](http://docs.scipy.org/doc/numpy/reference/swig.html)。这个接口文件定义了许多所谓的typemaps，支持了Numpy数组和C-Arrays的转化。在接下来的例子中，我们将快速看一下typemaps实际是如何工作的。\n",
    "\n",
    "我们有相同的`cos_doubles`函数，在ctypes例子中:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "void cos_doubles(double * in_array, double * out_array, int size);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#include <math.h>\n",
    "\n",
    "/*  Compute the cosine of each element in in_array, storing the result in\n",
    " *  out_array. */\n",
    "void cos_doubles(double * in_array, double * out_array, int size){\n",
    "    int i;\n",
    "    for(i=0;i<size;i++){\n",
    "        out_array[i] = cos(in_array[i]);\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用了SWIG接口文件封装了`cos_doubles_func`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "/*  Example of wrapping a C function that takes a C double array as input using\n",
    " *  numpy typemaps for SWIG. */\n",
    "\n",
    "%module cos_doubles\n",
    "%{\n",
    "    /* the resulting C file should be built as a python extension */\n",
    "    #define SWIG_FILE_WITH_INIT\n",
    "    /*  Includes the header in the wrapper code */\n",
    "    #include \"cos_doubles.h\"\n",
    "%}\n",
    "\n",
    "/*  include the numpy typemaps */\n",
    "%include \"numpy.i\"\n",
    "/*  need this for correct module initialization */\n",
    "%init %{\n",
    "    import_array();\n",
    "%}\n",
    "\n",
    "/*  typemaps for the two arrays, the second will be modified in-place */\n",
    "%apply (double* IN_ARRAY1, int DIM1) {(double * in_array, int size_in)}\n",
    "%apply (double* INPLACE_ARRAY1, int DIM1) {(double * out_array, int size_out)}\n",
    "\n",
    "/*  Wrapper for cos_doubles that massages the types */\n",
    "%inline %{\n",
    "    /*  takes as input two numpy arrays */\n",
    "    void cos_doubles_func(double * in_array, int size_in, double * out_array, int size_out) {\n",
    "        /*  calls the original funcion, providing only the size of the first */\n",
    "        cos_doubles(in_array, out_array, size_in);\n",
    "    }\n",
    "%}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 要使用Numpy的typemaps, 我们需要包含`numpy.i`文件。\n",
    "- 观察一下对`import_array()`的调用，这个模块我们已经在Numpy-C-API例子中遇到过。\n",
    "- 因为类型映射只支持ARRAY、SIZE的签名，我们需要将cos_doubles封装为cos_doubles_func，接收两个数组包括大小作为输入。\n",
    "- 与SWIG不同的是, 我们并没有包含`cos_doubles.h`头部，我们并不需要暴露给Python，因为，我们通过`cos_doubles_func`暴露了相关的功能。 \n",
    "\n",
    "并且，和之前一样，我们可以用`distutils`来封装这个函数:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from distutils.core import setup, Extension\n",
    "import numpy\n",
    "\n",
    "setup(ext_modules=[Extension(\"_cos_doubles\",\n",
    "      sources=[\"cos_doubles.c\", \"cos_doubles.i\"],\n",
    "      include_dirs=[numpy.get_include()])])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "和前面一样，我们需要用`include_dirs`来制定位置。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "$ ls\n",
    "cos_doubles.c  cos_doubles.h  cos_doubles.i  numpy.i  setup.py  test_cos_doubles.py\n",
    "$ python setup.py build_ext -i\n",
    "running build_ext\n",
    "building '_cos_doubles' extension\n",
    "swigging cos_doubles.i to cos_doubles_wrap.c\n",
    "swig -python -o cos_doubles_wrap.c cos_doubles.i\n",
    "cos_doubles.i:24: Warning(490): Fragment 'NumPy_Backward_Compatibility' not found.\n",
    "cos_doubles.i:24: Warning(490): Fragment 'NumPy_Backward_Compatibility' not found.\n",
    "cos_doubles.i:24: Warning(490): Fragment 'NumPy_Backward_Compatibility' not found.\n",
    "creating build\n",
    "creating build/temp.linux-x86_64-2.7\n",
    "gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include -I/home/esc/anaconda/include/python2.7 -c cos_doubles.c -o build/temp.linux-x86_64-2.7/cos_doubles.o\n",
    "gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include -I/home/esc/anaconda/include/python2.7 -c cos_doubles_wrap.c -o build/temp.linux-x86_64-2.7/cos_doubles_wrap.o\n",
    "In file included from /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1722,\n",
    "                 from /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:17,\n",
    "                 from /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/arrayobject.h:15,\n",
    "                 from cos_doubles_wrap.c:2706:\n",
    "/home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/npy_deprecated_api.h:11:2: warning: #warning \"Using deprecated NumPy API, disable it by #defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\"\n",
    "gcc -pthread -shared build/temp.linux-x86_64-2.7/cos_doubles.o build/temp.linux-x86_64-2.7/cos_doubles_wrap.o -L/home/esc/anaconda/lib -lpython2.7 -o /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/swig_numpy/_cos_doubles.so\n",
    "$ ls\n",
    "build/         cos_doubles.h  cos_doubles.py    cos_doubles_wrap.c  setup.py\n",
    "cos_doubles.c  cos_doubles.i  _cos_doubles.so*  numpy.i             test_cos_doubles.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "并且，和前面一样，我们来验证一下它工作正常:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pylab\n",
    "import cos_doubles\n",
    "\n",
    "x = np.arange(0, 2 * np.pi, 0.1)\n",
    "y = np.empty_like(x)\n",
    "\n",
    "cos_doubles.cos_doubles_func(x, y)\n",
    "pylab.plot(x, y)\n",
    "pylab.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](http://www.scipy-lectures.org/_images/test_cos_doubles1.png)\n",
    "\n",
    "## 2.8.5 Cython\n",
    "\n",
    "Cython既是写C扩展的类Python语言，也是这种语言的编译器。Cython语言是Python的超集，带有额外的结构，允许你调用C函数和C类型的注释变量和类属性。在这个意义上，可以称之为带有类型的Python。\n",
    "\n",
    "除了封装原生代码的基础应用案例，Cython也支持额外的应用案例，即交互优化。从根本上来说，从纯Python脚本开始，向瓶颈代码逐渐增加Cython类型来优化那些确实有影响的代码。\n",
    "\n",
    "在这种情况下，与SWIG很相似，因为代码可以自动生成，但是，从另一个角度来说，又与ctypes很类似，因为，封装代码（大部分）是用Python写的。\n",
    "\n",
    "尽管其他自动生成代码的解决方案很难debug（比如SWIG），Cython有一个GNU debugger扩展来帮助debug Python，Cython和C代码。\n",
    "\n",
    "---\n",
    "**注意** 自动生成的C代码使用Python-C-Api。\n",
    "\n",
    "---\n",
    "\n",
    "**优点**\n",
    "- 类Python语言来写扩展\n",
    "- 自动生成代码\n",
    "- 支持增量优化\n",
    "- 包含一个GNU debugger扩展\n",
    "- 支持C++ (从版本0.13)\n",
    "**不足**\n",
    "- 必须编译\n",
    "- 需要额外的库 ( 只是在build的时候, 在这个问题中，可以通过运送生成的C文件来克服)\n",
    "\n",
    "### 2.8.5.1 例子\n",
    "\n",
    "`cos_module`的主要Cython代码包含在文件`cos_module.pyx`中:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "\"\"\" Example of wrapping cos function from math.h using Cython. \"\"\"\n",
    "\n",
    "cdef extern from \"math.h\":\n",
    "    double cos(double arg)\n",
    "\n",
    "def cos_func(arg):\n",
    "    return cos(arg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意额外的关键词，比如`cdef`和`extern`。同时，`cos_func`也是纯Python。\n",
    "\n",
    "和前面一样，我们可以使用标准的`distutils`模块，但是，这次我们需要一些来自于`Cython.Distutils`的更多代码:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from distutils.core import setup, Extension\n",
    "from Cython.Distutils import build_ext\n",
    "\n",
    "setup(\n",
    "    cmdclass={'build_ext': build_ext},\n",
    "    ext_modules=[Extension(\"cos_module\", [\"cos_module.pyx\"])]\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "编译这个模块:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "$ cd advanced/interfacing_with_c/cython\n",
    "$ ls\n",
    "cos_module.pyx  setup.py\n",
    "$ python setup.py build_ext --inplace\n",
    "running build_ext\n",
    "cythoning cos_module.pyx to cos_module.c\n",
    "building 'cos_module' extension\n",
    "creating build\n",
    "creating build/temp.linux-x86_64-2.7\n",
    "gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/include/python2.7 -c cos_module.c -o build/temp.linux-x86_64-2.7/cos_module.o\n",
    "gcc -pthread -shared build/temp.linux-x86_64-2.7/cos_module.o -L/home/esc/anaconda/lib -lpython2.7 -o /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/cython/cos_module.so\n",
    "$ ls\n",
    "build/  cos_module.c  cos_module.pyx  cos_module.so*  setup.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "并且运行:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "In [1]: import cos_module\n",
    "\n",
    "In [2]: cos_module?\n",
    "Type:       module\n",
    "String Form:<module 'cos_module' from 'cos_module.so'>\n",
    "File:       /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/cython/cos_module.so\n",
    "Docstring:  <no docstring>\n",
    "\n",
    "In [3]: dir(cos_module)\n",
    "Out[3]:\n",
    "['__builtins__',\n",
    " '__doc__',\n",
    " '__file__',\n",
    " '__name__',\n",
    " '__package__',\n",
    " '__test__',\n",
    " 'cos_func']\n",
    "\n",
    "In [4]: cos_module.cos_func(1.0)\n",
    "Out[4]: 0.5403023058681398\n",
    "\n",
    "In [5]: cos_module.cos_func(0.0)\n",
    "Out[5]: 1.0\n",
    "\n",
    "In [6]: cos_module.cos_func(3.14159265359)\n",
    "Out[6]: -1.0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "并且，测试一下强壮性，我们可以看到我们得到了更好的错误信息:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "In [7]: cos_module.cos_func('foo')\n",
    "---------------------------------------------------------------------------\n",
    "TypeError                                 Traceback (most recent call last)\n",
    "<ipython-input-7-11bee483665d> in <module>()\n",
    "----> 1 cos_module.cos_func('foo')\n",
    "\n",
    "/home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/cython/cos_module.so in cos_module.cos_func (cos_module.c:506)()\n",
    "\n",
    "TypeError: a float is required"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "此外，不需要Cython完全传输到C math库的声明，上面的代码可以简化为:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "\"\"\" Simpler example of wrapping cos function from math.h using Cython. \"\"\"\n",
    "\n",
    "from libc.math cimport cos\n",
    "\n",
    "def cos_func(arg):\n",
    "    return cos(arg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这种情况下，`cimport`语句用于导入`cos`函数。\n",
    "\n",
    "### 2.8.5.2 Numpy支持\n",
    "\n",
    "Cython通过`numpy.pyx`文件支持Numpy，允许你为你的Cython代码添加Numpy数组类型，即就像指定变量`i`是`int`类型，你也可以指定变量`a`是带有给定的`dtype`的`numpy.ndarray`。同时，同时特定的优化比如边际检查也是支持的。看一下[Cython文档](http://docs.cython.org/src/tutorial/numpy.html)的对应部分。如果你想要将Numpy数组作为C数组传递给Cython封装的C函数，在[Cython wiki](http://wiki.cython.org/tutorials/NumpyPointerToC)上有对应的部分。\n",
    "\n",
    "在下面的例子中，我们将演示如何用Cython来封装类似的`cos_doubles`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "void cos_doubles(double * in_array, double * out_array, int size);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#include <math.h>\n",
    "\n",
    "/*  Compute the cosine of each element in in_array, storing the result in\n",
    " *  out_array. */\n",
    "void cos_doubles(double * in_array, double * out_array, int size){\n",
    "    int i;\n",
    "    for(i=0;i<size;i++){\n",
    "        out_array[i] = cos(in_array[i]);\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这个函数使用下面的Cython代码来封装`cos_doubles_func`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "\"\"\" Example of wrapping a C function that takes C double arrays as input using\n",
    "    the Numpy declarations from Cython \"\"\"\n",
    "\n",
    "# cimport the Cython declarations for numpy\n",
    "cimport numpy as np\n",
    "\n",
    "# if you want to use the Numpy-C-API from Cython\n",
    "# (not strictly necessary for this example, but good practice)\n",
    "np.import_array()\n",
    "\n",
    "# cdefine the signature of our c function\n",
    "cdef extern from \"cos_doubles.h\":\n",
    "    void cos_doubles (double * in_array, double * out_array, int size)\n",
    "\n",
    "# create the wrapper code, with numpy type annotations\n",
    "def cos_doubles_func(np.ndarray[double, ndim=1, mode=\"c\"] in_array not None,\n",
    "                     np.ndarray[double, ndim=1, mode=\"c\"] out_array not None):\n",
    "    cos_doubles(<double*> np.PyArray_DATA(in_array),\n",
    "                <double*> np.PyArray_DATA(out_array),\n",
    "                in_array.shape[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以使用`distutils`来编译:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from distutils.core import setup, Extension\n",
    "import numpy\n",
    "from Cython.Distutils import build_ext\n",
    "\n",
    "setup(\n",
    "    cmdclass={'build_ext': build_ext},\n",
    "    ext_modules=[Extension(\"cos_doubles\",\n",
    "                 sources=[\"_cos_doubles.pyx\", \"cos_doubles.c\"],\n",
    "                 include_dirs=[numpy.get_include()])],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "与前面的编译Numpy例子类似，我们需要`include_dirs`选项。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "$ ls\n",
    "cos_doubles.c  cos_doubles.h  _cos_doubles.pyx  setup.py  test_cos_doubles.py\n",
    "$ python setup.py build_ext -i\n",
    "running build_ext\n",
    "cythoning _cos_doubles.pyx to _cos_doubles.c\n",
    "building 'cos_doubles' extension\n",
    "creating build\n",
    "creating build/temp.linux-x86_64-2.7\n",
    "gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include -I/home/esc/anaconda/include/python2.7 -c _cos_doubles.c -o build/temp.linux-x86_64-2.7/_cos_doubles.o\n",
    "In file included from /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1722,\n",
    "                 from /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:17,\n",
    "                 from /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/arrayobject.h:15,\n",
    "                 from _cos_doubles.c:253:\n",
    "/home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/npy_deprecated_api.h:11:2: warning: #warning \"Using deprecated NumPy API, disable it by #defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\"\n",
    "/home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/__ufunc_api.h:236: warning: ‘_import_umath’ defined but not used\n",
    "gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include -I/home/esc/anaconda/include/python2.7 -c cos_doubles.c -o build/temp.linux-x86_64-2.7/cos_doubles.o\n",
    "gcc -pthread -shared build/temp.linux-x86_64-2.7/_cos_doubles.o build/temp.linux-x86_64-2.7/cos_doubles.o -L/home/esc/anaconda/lib -lpython2.7 -o /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/cython_numpy/cos_doubles.so\n",
    "$ ls\n",
    "build/  _cos_doubles.c  cos_doubles.c  cos_doubles.h  _cos_doubles.pyx  cos_doubles.so*  setup.py  test_cos_doubles.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "和前面一样，我们来验证一下它是有效的:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pylab\n",
    "import cos_doubles\n",
    "\n",
    "x = np.arange(0, 2 * np.pi, 0.1)\n",
    "y = np.empty_like(x)\n",
    "\n",
    "cos_doubles.cos_doubles_func(x, y)\n",
    "pylab.plot(x, y)\n",
    "pylab.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](http://www.scipy-lectures.org/_images/test_cos_doubles2.png)\n",
    "\n",
    "### 2.8.6 总结\n",
    "\n",
    "这个部分演示了四种与原生代码交互的技术。下表概述了这些技术的一些方面。\n",
    "\n",
    "|x|Part of CPython|Compiled|Autogenerated|Numpy Support|\n",
    "|---|---|---|---|---|\n",
    "|Python-C-API|True|True|False|True|\n",
    "|Ctypes|True|False|False|True|\n",
    "|Swig|False|True|True|True|\n",
    "|Cython|False|True|True|True|\n",
    "\n",
    "在上面的技术中，Cython是最现代最高级的。特别是，通过为Python代码添加类型来增量优化代码的技术是惟一的。\n",
    "\n",
    "## 2.8.7 Further Reading and References\n",
    "\n",
    "[Gaël Varoquaux关于避免数据复制的博客](http://gael-varoquaux.info/blog/?p=157)给出了一些如何精明的处理内存管理的见解。如果你在大数据量时出现问题，可以回到这里寻找一些灵感。\n",
    "\n",
    "## 2.8.8 练习\n",
    "\n",
    "因为这是一个新部分，练习更像是一个接下来应该查看什么的指示器，因此，看一下那些你觉得更有趣的部分。如果你有关于练习更好点子，请告诉我们！\n",
    "\n",
    "- 下载每个例子的源码，并且在你的机器上运行并编译他们。\n",
    "- 对每个例子做一些修改，并且自己验证一下是否有效。 ( 比如，将cos改为sin。)\n",
    "- 绝大多数例子，特别是包含了Numpy的例子，可能还是比较脆弱，对输入错误反应较差。找一些方法来让例子崩溃，找出问题所在，并且设计潜在的解决方案。这些是有些点子:\n",
    "    - 数字溢出\n",
    "    - 输入输出数组长度不一致\n",
    "    - 多维度数据\n",
    "    - 空数组\n",
    "    - non-double类型数组\n",
    "    - \n",
    "- 使用`%timeit`IPython魔法函数来测量不同解决方案的执行时间\n",
    "\n",
    "### 2.8.8.1 Python-C-API\n",
    "\n",
    "- 修改Numpy例子以便函数有两个输入参数，第二个参数是预分配输出数组，让它与其他的Numpy例子一致。\n",
    "- 修改这个例子，以便这个函数只有一个输入数组，在原地修改这个函数。\n",
    "- 试着用新的[Numpy迭代协议](http://docs.scipy.org/doc/numpy/reference/c-api.iterator.html)修改例子。如果你刚好获得了一个可用的解决方案，请将其提交一个请求到github。\n",
    "- 你可能注意到了，Numpy-C-API例子只是Numpy例子没有封装`cos_doubles`但是直接将`cos`函数应用于Numpy数组的元素上。这样做与其他技术相比有什么优势。\n",
    "- 你可以只用Numpy-C-API来封装`cos_doubles`。你可能需要确保数组有正确的类型，并且是单维度和内存临近。\n",
    "\n",
    "### 2.8.8.2 Ctypes\n",
    "\n",
    "- 修改Numpy例子以便`cos_doubles_func`为你处理预分配，让它更像Numpy-C-API例子。\n",
    "\n",
    "### 2.8.8.3. SWIG\n",
    "\n",
    "- 看一下SWIG自动生成的代码，你能理解多少?\n",
    "- 修改Numpy例子，以便`cos_doubles_func`为你处理预处理，让它更像Numpy-C-API例子。\n",
    "- 修改`cos_doubles` C 函数，以便它返回分配的数组。你可以用SWIG typemaps类封装吗? 如果不可以，为什么不可以? 对于这种特殊的情况有没有什么变通方法? (提示: 你知道输出数组的大小, 因此，可以从返回的`double \\*`构建Numpy数组。\n",
    "\n",
    "### 2.8.8.4 Cython\n",
    "\n",
    "- 看一下Cython自动生成的代码。仔细看一下Cython插入的一些评论。你能看到些什么?\n",
    "- 看一下Cython文档中[与Numpy工作](http://docs.cython.org/src/tutorial/numpy.html)的部分, 学习一下如何使用Numpy增量优化python脚本。\n",
    "- 修改Numpy例子，以便`cos_doubles_func`为你处理预处理，让它更像Numpy-C-API例子。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
